<?php
declare(strict_types=1);

namespace App\Security;
use Hyperf\Logger\Logger;
use Hyperf\Redis\Redis;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Di\Annotation\Inject;
class LoginSecurity{

    const MAX_FAILED_ATTEMPTS_PER_IP = 5;     // 单IP最大尝试次数
    const LOCK_DURATION_MINUTES = 5;
    public const REDIS_PREFIX = 'login_security:';

    #[Inject]
    protected Redis $redis;

    #[Inject]
    protected RequestInterface $request;

    /**
     * 获取客户端IP
     */
    protected function getClientIp(): string
    {
        $headers = $this->request->getHeader('x-forwarded-for');
        if (!empty($headers)) {
            return $headers[0];
        }

        $serverParams = $this->request->getServerParams();
        return $serverParams['remote_addr'] ?? '127.0.0.1';
    }

    /**
     * 生成IP失败次数键
     */
    protected function getFailedKey(string $ip): string
    {
        return self::REDIS_PREFIX . 'attempts:' . md5($ip);
    }
    /**
     * 增加IP失败次数
     */
    public function incrementFailedAttempts(string $key): int
    {

        if ($this->getFailedAttempts($key) <= self::MAX_FAILED_ATTEMPTS_PER_IP) {
            $redisKey = $this->getFailedKey($key);
            $this->redis->pipeline()
                ->incr($redisKey)
                ->expire($redisKey, self::LOCK_DURATION_MINUTES * 60)
                ->exec();
        }

        return $this->getFailedAttempts($key);
    }
    public function getFailedAttempts(string $key): int
    {
        $redisKey = $this->getFailedKey($key);
        return (int) $this->redis->get($redisKey) ?: 0;
    }
    public function clearFailedAttempts(string $key): void
    {
        $redisKey = $this->getFailedKey($key);
        $this->redis->del($redisKey);
    }
    public function checkLocked(string $key)
    {
        $num = $this->incrementFailedAttempts($key);
        return $num > self::MAX_FAILED_ATTEMPTS_PER_IP;
    }
}